import struct
import ctypes
import pylynchipsdk as sdk
from common.python.infer_process import *
from common.python.callback_data_struct import *
from ctypes import *
import ipe_param_face_recongnition

pythonapi.PyCapsule_GetPointer.restype = c_void_p
pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p]

class FaceDetectBox(ctypes.Structure):
    _fields_ = [
        ("xmin", ctypes.c_float),
        ("ymin", ctypes.c_float),
        ("xmax", ctypes.c_float),
        ("ymax", ctypes.c_float),
        ("area", ctypes.c_float),
        ("score", ctypes.c_float),
        ("id", ctypes.c_int),
        ("landmark", ctypes.ARRAY(ctypes.c_float, 10)),
    ]


class FaceDetectInfo(ctypes.Structure):
    _fields_ = [
        ("boxesNum", ctypes.c_uint16),
        ("boxes", ctypes.ARRAY(FaceDetectBox, 256)),
    ]


class bbox(ctypes.Structure):
    _fields_ = [
        ("xmin", ctypes.c_uint32),
        ("ymin", ctypes.c_uint32),
        ("xmax", ctypes.c_uint32),
        ("ymax", ctypes.c_uint32),
        ("score", ctypes.c_float),
        ("id", ctypes.c_int),
        ("label", ctypes.c_char * 64),
    ]


class Box(ctypes.Structure):
    _fields_ = [("boxesnum", ctypes.c_uint32), ("boxes", ctypes.ARRAY(bbox, 256))]


class FaceRect:
    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h

    def ensure_even(self, n):
        if n % 2 != 0:
            return n + 1
        return n

    def make_even(self):
        self.x = self.ensure_even(self.x)
        self.y = self.ensure_even(self.y)
        self.w = self.ensure_even(self.w)
        self.h = self.ensure_even(self.h)


def extend_face_box(face_box, image_pixel_w, image_pixel_h):
    w = face_box.xmax - face_box.xmin
    h = face_box.ymax - face_box.ymin

    xmin = max(int(face_box.xmin), 0)
    ymin = max(int(face_box.ymin), 0)
    xmax = min(int(face_box.xmax), int(image_pixel_w - 1))
    ymax = min(int(face_box.ymax), int(image_pixel_h - 1))

    rect = FaceRect(xmin, ymin, xmax - xmin, ymax - ymin)
    rect.make_even()
    return rect


def check_face_box(face_box):
    w = face_box.xmax - face_box.xmin
    h = face_box.ymax - face_box.ymin

    if w <= 20 or h <= 20:
        return False

    for i in range(10):
        if face_box.landmark[i] < 0:
            return False

    return True


class FeatureData:
    def __init__(self, num_feature=0):
        self.numFeature = num_feature
        self.features = None

    @staticmethod
    def get_affine_transform(points_from, points_to, num_point):
        ma = np.zeros((4, 4))
        mb = np.zeros(4)
        mm = np.zeros(4)

        for i in range(num_point):
            ma[0][0] += (
                points_from[0] * points_from[0] + points_from[1] * points_from[1]
            )
            ma[0][2] += points_from[0]
            ma[0][3] += points_from[1]

            mb[0] += points_from[0] * points_to[0] + points_from[1] * points_to[1]
            mb[1] += points_from[0] * points_to[1] - points_from[1] * points_to[0]
            mb[2] += points_to[0]
            mb[3] += points_to[1]

            points_from = points_from[2:]
            points_to = points_to[2:]

        ma[1][1] = ma[0][0]
        ma[2][1] = ma[1][2] = -ma[0][3]
        ma[3][1] = ma[1][3] = ma[2][0] = ma[0][2]
        ma[2][2] = ma[3][3] = float(num_point)
        ma[3][0] = ma[0][3]

        mai = np.linalg.inv(ma)
        mm[0] = (
            mai[0][0] * mb[0]
            + mai[0][1] * mb[1]
            + mai[0][2] * mb[2]
            + mai[0][3] * mb[3]
        )
        mm[1] = (
            mai[1][0] * mb[0]
            + mai[1][1] * mb[1]
            + mai[1][2] * mb[2]
            + mai[1][3] * mb[3]
        )
        mm[2] = (
            mai[2][0] * mb[0]
            + mai[2][1] * mb[1]
            + mai[2][2] * mb[2]
            + mai[2][3] * mb[3]
        )
        mm[3] = (
            mai[3][0] * mb[0]
            + mai[3][1] * mb[1]
            + mai[3][2] * mb[2]
            + mai[3][3] * mb[3]
        )

        tm = [mm[0], -mm[1], mm[1], mm[2], mm[3], mm[0]]
        tmatrix = np.array([[mm[0], -mm[1], mm[2]], [mm[1], mm[0], mm[3]]])

        return tm, tmatrix

    @staticmethod
    def get_transform_mat(landmark, rect):
        point_landmark = np.array(
            [
                30.2946 + 8.0,
                51.6963,
                65.5318 + 8.0,
                51.5014,
                48.0252 + 8.0,
                71.7366,
                33.5493 + 8.0,
                92.3655,
                62.729904 + 8.0,
                92.2041,
            ]
        )
        num_landmarks = 5

        landmarks = np.zeros(num_landmarks * 2)
        for i in range(num_landmarks):
            landmarks[2 * i] = landmark[2 * i] - rect.x
            landmarks[2 * i + 1] = landmark[2 * i + 1] - rect.y

            landmarks[2 * i] = max(0, landmarks[2 * i])
            landmarks[2 * i + 1] = max(0, landmarks[2 * i + 1])

        tm, matrix = FeatureData.get_affine_transform(
            landmarks, point_landmark, num_landmarks
        )

        return matrix


def dump_box_json(output_path, c_box):
    box_dict = {"boxesnum": c_box.boxesnum, "boxes": []}
    for i in range(c_box.boxesnum):
        bbox_instance = c_box.boxes[i]
        bbox_dict = {
            "xmin": bbox_instance.xmin,
            "ymin": bbox_instance.ymin,
            "xmax": bbox_instance.xmax,
            "ymax": bbox_instance.ymax,
            "score": bbox_instance.score,
            "label": bbox_instance.label.decode("utf-8"),
        }
        box_dict["boxes"].append(bbox_dict)

    # Serialize the dictionary to JSON
    with open(output_path, "a", encoding="utf-8") as f:
        f.seek(0, os.SEEK_END)
        file_size = f.tell()
        if file_size < 500 * 1024 * 1024:
            json.dump(box_dict, f, indent=2, ensure_ascii=False)
            f.write("\n")
        else:
            print("only support 500M.", output_path, "now file size:", file_size)


def cal_event_cost(args):
    time, ret = sdk.lyn_event_elapsed_time(args[0], args[1])
    common.error_check(ret, "lyn_event_elapsed_time")
    if args[2]:
        args[2][0] += 1
        args[2][1] += time
    return 0


def face_recog_callback(args):
    pDevCosineBuf, pHostCosineBuf_c, faceIndexVec,\
        pDevFeatureApuBuf, pHostBoxesInfo, face_lib_labels = args
    faceCount = len(faceIndexVec)
    int16_array = (ctypes.c_int16 * faceCount).from_address(pHostCosineBuf_c)
    float_array = (ctypes.c_float * faceCount).from_address(pHostCosineBuf_c + ctypes.sizeof(ctypes.c_int16) * faceCount)
    for i in range(faceCount):
        face_index = faceIndexVec[i]
        idx = int(float_array[i])
        score = common.half2float(int16_array[i])
        if idx >= 0 and idx < len(face_lib_labels):
            name = "unknown"
            if score > 5:
                name = face_lib_labels[idx]
            pHostBoxesInfo.boxes[face_index].label = name.encode("utf-8")

    sdk.lyn_free(pDevCosineBuf)
    sdk.lyn_free(pDevFeatureApuBuf)
    return 0



def lyn_free_callback(args):
    for i in args:
        common.error_check(sdk.lyn_free(i))
    return 0


def ipe_destory_callback(args):
    for i in args:
        i.destory()
    return 0


def return_buffer(callback_data) -> int:
    """将buffer还回buffer pool中

    Args:
        callback_data (list): 大小为2的list：[buffer pool, buffer]

    Returns:
        _type_: integer，默认为0
    """
    callback_data[0].push(callback_data[1])
    return 0


def free_device_memory(mem) -> int:
    """释放lyn_malloc申请的内存

    Args:
        mem (): device侧的内存地址

    Returns:
        _type_: integer，默认为0
    """
    sdk.lyn_free(mem)
    return 0


def return_buffers(callback_data) -> int:
    """将列表中的buffer还回buffer pool中

    Args:
        callback_data (list): 大小为2的list：[buffer pool, buffers]，第二个元素为buffer组成的list

    Returns:
        _type_: integer，默认为0
    """
    for buffer in callback_data[1]:
        callback_data[0].push(buffer)
    return 0


def put_to_queue(callback_data) -> int:
    """将元素放入queue中

    Args:
        callback_data (list): 大小为2的list：[queue, element]

    Returns:
        _type_: integer，默认为0
    """
    callback_data[0].put(callback_data[1])
    return 0


def get_rect(bbox_instance, width, height) -> Tuple[Tuple[int, int, int, int], bool]:
    """从Box实例中获取合法的矩形的大小和位置信息

    Args:
        bbox_instance (Box): Box实例
        width (integer): 视频分辨率的宽度
        height (integer):  视频分辨率的长度

    Returns:
        Tuple[Tuple[int, int, int, int], bool]: x,y,w,h和是否合法
    """
    x = bbox_instance.xmin
    y = bbox_instance.ymin
    w = bbox_instance.xmax - bbox_instance.xmin
    h = bbox_instance.ymax - bbox_instance.ymin
    # 确保矩形的x，y在视频长宽的范围内
    x = max(0, x)
    y = max(0, y)
    x = min(width - 8 - 1, x)
    y = min(height - 8 - 1, y)
    w = min(w, width - x - 1)
    h = min(h, height - y - 1)
    is_valid = w >= 8 and h >= 8

    # 确保x,y,w,h都是偶数
    def ensure_even(n):
        return n + 1 if n % 2 != 0 else n

    result_tuple = tuple(ensure_even(n) for n in (x, y, w, h))
    return result_tuple, is_valid


class Encoder:
    """视频编码类"""

    def __init__(self, ctx, framepool, vdec_out_info, output_path):
        self.frame_pool = framepool
        sdk.lyn_set_current_context(ctx)
        self.venc_recv_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        self.venc_send_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        self.enc_head_flag = True
        venc_attr = sdk.lyn_venc_attr_t()
        ret = sdk.lyn_venc_set_default_params(venc_attr)
        common.error_check(ret, "lyn_venc_set_default_params")
        venc_attr.codec_type = sdk.lyn_codec_id_t.LYN_CODEC_ID_H264
        self.vdec_out_info = vdec_out_info
        venc_attr.width = self.vdec_out_info.width
        venc_attr.height = self.vdec_out_info.height
        venc_attr.bit_depth = 8
        venc_attr.bframes_num = 0
        venc_attr.pframes_num = 5
        venc_attr.input_format = sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12
        venc_attr.target_bitrate = 6000000
        venc_attr.level = -1
        self.venc_handle, ret = sdk.lyn_venc_open(venc_attr)
        common.error_check(ret, "lyn_venc_open")
        self.output_path = output_path
        self.venc_recv_pool = bufferpool.buffer_pool(
            self.vdec_out_info.predict_buf_size, 5
        )

    def encode(
        self,
        frame,
    ):
        if self.enc_head_flag:
            self.enc_head_flag = False
            enc_packet = sdk.lyn_packet_t()
            enc_packet.size = self.vdec_out_info.predict_buf_size
            enc_packet.data = self.venc_recv_pool.pop()
            encode_data = save_file_cb_data()
            encode_data.output_path = self.output_path
            encode_data.packet = enc_packet
            encode_data.recv_pool = self.venc_recv_pool
            encode_data.file_path = self.output_path
            ret = sdk.lyn_venc_get_paramsset_async(
                self.venc_recv_stream, self.venc_handle, enc_packet
            )
            common.error_check(ret, "lyn_venc_get_paramsset_async")
            ret = sdk.lyn_stream_add_callback(
                self.venc_recv_stream, save_file_cb, encode_data
            )
            common.error_check(ret, "lyn_stream_add_callback")

        ret = sdk.lyn_venc_sendframe_async(
            self.venc_send_stream, self.venc_handle, frame
        )
        common.error_check(ret, "lyn_venc_sendframe_async")
        enc_packet = sdk.lyn_packet_t()
        enc_packet.size = self.vdec_out_info.predict_buf_size
        enc_packet.eos = frame.eos
        enc_packet.data = self.venc_recv_pool.pop()
        encode_data = save_file_cb_data()
        encode_data.packet = enc_packet
        encode_data.recv_pool = self.venc_recv_pool
        encode_data.file_path = self.output_path
        encode_data.output_path = self.output_path
        ret = sdk.lyn_stream_add_callback(
            self.venc_send_stream,
            free_to_pool_callback,
            [self.frame_pool, frame.data],
        )
        common.error_check(ret, "lyn_stream_add_callback")
        ret = sdk.lyn_venc_recvpacket_async(
            self.venc_recv_stream, self.venc_handle, enc_packet
        )
        common.error_check(ret, "lyn_venc_recvpacket_async")
        ret = sdk.lyn_stream_add_callback(
            self.venc_recv_stream, save_file_cb, encode_data
        )
        common.error_check(ret, "lyn_stream_add_callback")

    def __del__(self):
        sdk.lyn_synchronize_stream(self.venc_recv_stream)
        sdk.lyn_synchronize_stream(self.venc_send_stream)
        sdk.lyn_destroy_stream(self.venc_recv_stream)
        sdk.lyn_destroy_stream(self.venc_send_stream)
        sdk.lyn_venc_close(self.venc_handle)
        


class OpencvWindow:
    """opencv窗口类"""

    def __init__(self, frame_pool, video_frame, vdec_out_info):
        self.frame_pool = frame_pool
        self.video_frame = video_frame
        self.vdec_out_info = vdec_out_info

    def show(self, frame):
        data = np.empty(frame.size, dtype=np.uint8)
        data_ptr = sdk.lyn_numpy_to_ptr(data)
        ret = sdk.lyn_memcpy(
            data_ptr,
            frame.data,
            frame.size,
            sdk.lyn_memcpy_dir_t.ServerToClient,
        )
        common.error_check(ret, "lyn_memcpy")
        yuvImg = np.reshape(
            data, (self.vdec_out_info.height * 3 // 2, self.vdec_out_info.width)
        ).astype(np.uint8)
        if frame.eos:
            self.video_frame.put([yuvImg, frame.eos])
        else:
            self.video_frame.queue.clear()
            self.video_frame.put([yuvImg, frame.eos])
        self.frame_pool.push(frame.data)
        return 0


class FaceRecognition:
    """人脸识别类"""

    def __init__(self):
        self.device_id = 0
        self.channel_id = 0
        self.model_path = ""
        self.show_type = 1
        self.video_frame = ""
        self.input_file = ""
        self.output_path = ""
        self.post_plugin_path = ""
        self.osd_plugin_path = ""
        self.detect_model_path = ""
        self.recog_model_path = ""
        self.__demux_hdl = ""
        self.__vdec_hdl = ""
        self.faces_config = None
        self.print_stats = 0
        self.cost_info = {}

    def init(self):
        self.__ctx, ret = sdk.lyn_create_context(self.device_id)
        self.use_local_input = os.path.exists(self.input_file)

        common.error_check(ret, "lyn_create_context")

        if self.print_stats > 0:
            self.cost_info = {
                "face_detect_preprocess": [0, 0],
                "face_detect_infer": [0, 0],
                "face_detect_postprocess": [0, 0],
                "face_feature_preprocess": [0, 0],
                "face_feature_infer": [0, 0],
                "face_feature_postprocess": [0, 0],
            }
        self.init_demuxer()
        self.init_decoder()
        self.create_queue()
        self.register_plugin()

    def init_decoder(self):
        sdk.lyn_set_current_context(self.__ctx)
        self.__vdec_attr = sdk.lyn_vdec_attr_t()
        self.__vdec_attr.codec_id = self.codec_para.codec_id
        self.__vdec_attr.output_fmt = sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12
        self.__vdec_attr.scale = sdk.lyn_scale_t.SCALE_NONE
        self.__vdec_hdl, ret = sdk.lyn_vdec_open(self.__vdec_attr)
        common.error_check(ret, "lyn_vdec_open")
        self.vdec_out_info, ret = sdk.lyn_vdec_get_out_info(
            self.codec_para, self.__vdec_attr
        )
        common.error_check(ret, "lyn_vdec_get_out_info")
        self.frame_pool = bufferpool.buffer_pool(self.vdec_out_info.predict_buf_size, 5)

    def init_demuxer(self):
        self.__demux_hdl, ret = sdk.lyn_demux_open(self.input_file)
        common.error_check(ret, "lyn_demux_open")
        self.codec_para, ret = sdk.lyn_demux_get_codec_para(self.__demux_hdl)
        common.error_check(ret, "lyn_demux_get_codec_para")

    def create_queue(self):
        self.__send_queue = block_queue()
        self.__recv_queue = block_queue()
        self.__detect_result_queue = block_queue()
        self.__recognize_result_queue = block_queue()

    def register_plugin(self):
        self.post_plugin, ret = sdk.lyn_plugin_register(self.post_plugin_path)
        common.error_check(ret, "lyn_plugin_register")
        self.osd_plugin, ret = sdk.lyn_plugin_register(self.osd_plugin_path)
        common.error_check(ret, "lyn_plugin_register")

    def run(self, cancel_flag):
        self.__decode_send_thread = threading.Thread(
            target=self.decoder_send, args=(cancel_flag,)
        )
        self.__decode_send_thread.start()

        # 开启接收线程
        self.__decode_recv_thread = threading.Thread(
            target=self.decoder_recv, args=(cancel_flag,)
        )
        self.__decode_recv_thread.start()

        # 开启检测线程
        self.__detect_thread = threading.Thread(
            target=self.face_detect, args=(cancel_flag,)
        )
        self.__detect_thread.start()

        # 开启识别测线程
        self.__face_recognize_thread = threading.Thread(
            target=self.face_recognize, args=(cancel_flag,)
        )
        self.__face_recognize_thread.start()

        # 开启展示或保存线程
        self.__generate_result_thread = threading.Thread(
            target=self.generate_result, args=(cancel_flag,)
        )
        self.__generate_result_thread.start()

    def decoder_send(self, cancel_flag):
        # 设置上下文环境 创建发送stream
        sdk.lyn_set_current_context(self.__ctx)
        send_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        eos, reconnect = False, False
        while not eos:
            # 从解封装器读取一个包
            pkt, ret = sdk.lyn_demux_read_packet(self.__demux_hdl)
            # common.error_check(ret, "lyn_demux_read_packet")
            eos = pkt.eos
            reconnect = ret == 101003
            if reconnect:
                eos = False
                self.__send_queue.put((eos, reconnect))
                ret = sdk.lyn_vdec_send_packet_async(send_stream, self.__vdec_hdl, pkt)
                self.__decode_recv_thread.join()
                self.__generate_result_thread.join()
                self.frame_pool.free_buffers()
                sdk.lyn_vdec_close(self.__vdec_hdl)
                sdk.lyn_demux_close(self.__demux_hdl)

                self.init_demuxer()
                self.init_decoder()
                print(
                    f"cnannel {self.device_id}_{self.channel_id} changed resolution to: {self.vdec_out_info.width}, {self.vdec_out_info.height}"
                )

                self.__decode_recv_thread = threading.Thread(
                    target=self.decoder_recv, args=(cancel_flag,)
                )
                self.__decode_recv_thread.start()
                self.__generate_result_thread = threading.Thread(
                    target=self.generate_result, args=(cancel_flag,)
                )
                self.__generate_result_thread.start()
                continue

            if (eos or ret == lynEEOF) and not self.use_local_input:
                sdk.lyn_demux_close(self.__demux_hdl)
                time.sleep(500.0 / 1000)
                print("demux failed, reconnecting...")
                self.__demux_hdl, ret = sdk.lyn_demux_open(self.input_file)
                common.error_check(ret, "lyn_demux_open")
                eos = False
                continue

            # 发送给解码器解码
            ret = sdk.lyn_vdec_send_packet_async(send_stream, self.__vdec_hdl, pkt)
            common.error_check(ret, "lyn_vdec_send_packet_async")
            ret = sdk.lyn_synchronize_stream(send_stream)
            common.error_check(ret, "lyn_synchronize_stream")
            # 释放packet内存并通知接收结果
            if not eos:
                sdk.lyn_demux_free_packet(pkt)
            self.__send_queue.put((eos, reconnect))
            if cancel_flag.value:
                return

    def decoder_recv(self, cancel_flag):
        sdk.lyn_set_current_context(self.__ctx)
        recv_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        eos, reconnect = False, False
        while not eos:
            eos, reconnect = self.__send_queue.take()
            if reconnect:
                cb_data = recv_cb_data()
                cb_data.reconnect = reconnect
                ret = sdk.lyn_stream_add_callback(
                    recv_stream, put_to_queue, [self.__recv_queue, cb_data]
                )
                self.__send_queue.clear()
                break
            cb_data = recv_cb_data()
            cb_data.frame.eos = eos
            cb_data.frame.data = self.frame_pool.pop()
            cb_data.frame.size = self.vdec_out_info.predict_buf_size
            cb_data.frame_pool = self.frame_pool
            cb_data.block_queue = self.__recv_queue
            cb_data.video_frame = self.video_frame
            ret = sdk.lyn_vdec_recv_frame_async(
                recv_stream, self.__vdec_hdl, cb_data.frame
            )
            common.error_check(ret, "lyn_vdec_recv_frame_async")
            ret = sdk.lyn_stream_add_callback(
                recv_stream, put_to_queue, [self.__recv_queue, cb_data]
            )
            common.error_check(ret, "lyn_stream_add_callback")
            if cancel_flag.value:
                return

    def detect_ipe(self, ipe_stream, frame, ipe_out_data, model_width, model_height):
        pass

    def detect_callback(self, callback_data):
        """将后处理结果复制回来，将frame和box信息放入检测结果队列"""
        dst_img_size = ctypes.sizeof(FaceDetectInfo)
        host_buf_arr = np.zeros(dst_img_size, dtype=np.uint8)
        host_buf = sdk.lyn_numpy_to_ptr(host_buf_arr)
        ret = sdk.lyn_memcpy(
            host_buf,
            callback_data[1],
            dst_img_size,
            sdk.lyn_memcpy_dir_t.ServerToClient,
        )
        common.error_check(ret, "detect_callback lyn_memcpy")
        df = detect_frame()
        df.frame = callback_data[0]
        df.host_box_info = host_buf_arr
        self.__detect_result_queue.put(df)
        return 0

    def face_detect(self, cancel_flag):
        sdk.lyn_set_current_context(self.__ctx)
        ipe_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        apu_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        post_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        ipe_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        face_detect_model, ret = sdk.lyn_load_model(self.detect_model_path)
        common.error_check(ret, "lyn_load_model")

        ipe_event_begin, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        ipe_event_end, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event_begin, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event_end, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        post_event_begin, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        post_event_end, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")

        # 获取模型信息
        model_desc, ret = sdk.lyn_model_get_desc(face_detect_model)
        common.error_check(ret, "lyn_model_get_desc")
        batch_size = model_desc.inputTensorAttrArray[0].batchSize
        model_width = model_desc.inputTensorAttrArray[0].dims[2]
        model_height = model_desc.inputTensorAttrArray[0].dims[1]
        ipe_output_size = (
            model_width * model_height * model_desc.inputTensorAttrArray[0].dims[3]
        )
        apu_output_size = model_desc.outputDataLen
        apu_buffer_pool = bufferpool.buffer_pool(apu_output_size * batch_size, 5)
        ipe_buffer_pool = bufferpool.buffer_pool(ipe_output_size * batch_size, 5)
        # 初始化IPE
        ipeScrfd = ipe_param_face_recongnition.IpeParamScrfd(model_width, model_height)
        ipeScrfd.set_img_info(
            self.vdec_out_info.width,
            self.vdec_out_info.height,
            sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12,
        )

        pDevDetectInfo, ret = sdk.lyn_malloc(ctypes.sizeof(FaceDetectInfo))
        common.error_check(ret)
        pDevDetectInfo_ptr = pythonapi.PyCapsule_GetPointer(pDevDetectInfo, None)

        eos = False
        while not eos:
            cb_data = self.__recv_queue.take()
            if cb_data.reconnect:
                df = detect_frame()
                df.reconnect = cb_data.reconnect
                sdk.lyn_stream_add_callback(
                    post_stream, put_to_queue, [self.__detect_result_queue, df]
                )
                self.__recv_queue.clear()
                continue
            eos = cb_data.frame.eos
            ipe_buffer = ipe_buffer_pool.pop()
            apu_buffer = apu_buffer_pool.pop()

            sdk.lyn_record_event(ipe_stream, ipe_event_begin)
            ipeScrfd.calc_param(ipe_stream, cb_data.frame.data, ipe_buffer)
            sdk.lyn_record_event(ipe_stream, ipe_event_end)
            sdk.lyn_stream_add_callback(
                ipe_stream,
                cal_event_cost,
                [
                    ipe_event_begin,
                    ipe_event_end,
                    self.cost_info.get("face_detect_preprocess", []),
                ],
            )
            # self.detect_ipe(
            #     ipe_stream, cb_data.frame, ipe_buffer, model_width, model_height
            # )
            sdk.lyn_record_event(ipe_stream, ipe_event)
            sdk.lyn_stream_wait_event(apu_stream, ipe_event)

            sdk.lyn_record_event(apu_stream, apu_event_begin)
            sdk.lyn_execute_model_async(
                apu_stream, face_detect_model, ipe_buffer, apu_buffer, batch_size
            )
            sdk.lyn_record_event(apu_stream, apu_event_end)
            sdk.lyn_stream_add_callback(
                apu_stream,
                cal_event_cost,
                [
                    apu_event_begin,
                    apu_event_end,
                    self.cost_info.get("face_detect_infer", []),
                ],
            )

            sdk.lyn_record_event(apu_stream, apu_event)
            sdk.lyn_stream_wait_event(post_stream, apu_event)

            pDevDetectApuBuf_ptr = pythonapi.PyCapsule_GetPointer(apu_buffer, None)

            post_info = struct.pack(
                "4i2f2P",
                model_height,
                model_width,
                self.vdec_out_info.height,
                self.vdec_out_info.width,
                0.25,
                0.45,
                pDevDetectApuBuf_ptr,
                pDevDetectInfo_ptr,
            )

            sdk.lyn_record_event(post_stream, post_event_begin)
            common.error_check(
                sdk.lyn_plugin_run_async(
                    post_stream,
                    self.post_plugin,
                    "lynFacePostProcess",
                    post_info,
                    len(post_info),
                )
            )
            common.error_check(ret, "lyn_plugin_run_async")
            sdk.lyn_record_event(post_stream, post_event_end)
            sdk.lyn_stream_add_callback(
                post_stream,
                cal_event_cost,
                [
                    post_event_begin,
                    post_event_end,
                    self.cost_info.get("face_detect_postprocess", []),
                ],
            )

            sdk.lyn_stream_add_callback(
                post_stream, self.detect_callback, [cb_data.frame, pDevDetectInfo]
            )
            sdk.lyn_stream_add_callback(
                post_stream, return_buffer, [ipe_buffer_pool, ipe_buffer, "ipe_buffer"]
            )
            sdk.lyn_stream_add_callback(
                post_stream, return_buffer, [apu_buffer_pool, apu_buffer, "apu_buffer"]
            )
            common.print_frame_rate(
                f"{self.device_id}_{self.channel_id}", self.cost_info
            )
            if cancel_flag.value:
                break

        sdk.lyn_synchronize_stream(ipe_stream)
        sdk.lyn_synchronize_stream(apu_stream)
        sdk.lyn_synchronize_stream(post_stream)
        sdk.lyn_free(pDevDetectInfo)
        sdk.lyn_destroy_event(ipe_event)
        sdk.lyn_destroy_event(apu_event)
        sdk.lyn_destroy_stream(ipe_stream)
        sdk.lyn_destroy_stream(apu_stream)
        sdk.lyn_destroy_stream(post_stream)
        sdk.lyn_unload_model(face_detect_model)

        sdk.lyn_destroy_event(ipe_event_begin)
        sdk.lyn_destroy_event(ipe_event_end)
        sdk.lyn_destroy_event(apu_event_begin)
        sdk.lyn_destroy_event(apu_event_end)
        sdk.lyn_destroy_event(post_event_begin)
        sdk.lyn_destroy_event(post_event_end)

    def recognize_ipe(
        self, ipe_stream, frame, ipe_out_data, model_width, model_height, xywh
    ):
        pass

    def face_recognize(self, cancel_flag):
        sdk.lyn_set_current_context(self.__ctx)
        ipe_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        apu_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        osd_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")

        ipe_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        face_recognize_model, ret = sdk.lyn_load_model(self.recog_model_path)
        common.error_check(ret, "lyn_load_model")

        ipe_event_begin, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        ipe_event_end, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event_begin, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event_end, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        post_event_begin, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        post_event_end, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")

        ret = sdk.lyn_cosine_init()
        common.error_check(ret, "lyn_cosine_init")

        # 读取人脸库信息
        face_lib_features_host_np = np.fromfile(self.features_path, dtype=np.uint8)
        face_lib_features_host = sdk.lyn_numpy_to_ptr(face_lib_features_host_np)
        face_lib_features, ret = sdk.lyn_malloc(len(face_lib_features_host_np))
        common.error_check(ret)
        common.error_check(
            sdk.lyn_memcpy(
                face_lib_features,
                face_lib_features_host,
                len(face_lib_features_host_np),
                sdk.lyn_memcpy_dir_t.ClientToServer,
            )
        )

        # 获取模型信息
        model_desc, ret = sdk.lyn_model_get_desc(face_recognize_model)
        common.error_check(ret, "lyn_model_get_desc")
        batch_size = model_desc.inputTensorAttrArray[0].batchSize
        model_width = model_desc.inputTensorAttrArray[0].dims[2]
        model_height = model_desc.inputTensorAttrArray[0].dims[1]
        ipe_output_size = (
            model_width * model_height * model_desc.inputTensorAttrArray[0].dims[3]
        )
        apu_output_size = model_desc.outputDataLen
        ipe_buffer_pool = bufferpool.buffer_pool(ipe_output_size * batch_size, 25)

        pHostBoxesInfo = Box()
        eos = False
        while not eos:
            cb_data: detect_frame = self.__detect_result_queue.take()
            eos = cb_data.frame.eos
            if cb_data.reconnect:
                sdk.lyn_stream_add_callback(
                    osd_stream, put_to_queue, [self.__recognize_result_queue, cb_data]
                )
                self.__detect_result_queue.clear()
                continue
            host_buf = sdk.lyn_numpy_to_ptr(cb_data.host_box_info)
            host_buf_c = pythonapi.PyCapsule_GetPointer(host_buf, None)
            c_box = ctypes.cast(host_buf_c, ctypes.POINTER(FaceDetectInfo)).contents

            faceIndexVec = []
            pDevFeatureApuBuf = None
            if c_box.boxesNum > 0:
                pDevFeatureApuBuf, ret = sdk.lyn_malloc(
                    apu_output_size * c_box.boxesNum
                )
                common.error_check(ret)
            pHostBoxesInfo.boxesnum = c_box.boxesNum

            # 推理一帧中所有的人脸
            for i in range(c_box.boxesNum):

                bbox_instance = c_box.boxes[i]

                faceRect = extend_face_box(
                    bbox_instance, self.vdec_out_info.width, self.vdec_out_info.height
                )

                pHostBoxesInfo.boxes[i].xmin = faceRect.x
                pHostBoxesInfo.boxes[i].ymin = faceRect.y
                pHostBoxesInfo.boxes[i].xmax = faceRect.x + faceRect.w
                pHostBoxesInfo.boxes[i].ymax = faceRect.y + faceRect.h

                if check_face_box(bbox_instance):
                    ipe_buffer = ipe_buffer_pool.pop()

                    sdk.lyn_record_event(ipe_stream, ipe_event_begin)
                    ipeCrop = ipe_param_face_recongnition.IpeParamCrop(
                        faceRect.x, faceRect.y, faceRect.w, faceRect.h
                    )

                    pFaceCropIpeBufOut, ret = sdk.lyn_malloc(
                        faceRect.w * faceRect.h * 3
                    )
                    common.error_check(ret)
                    ipeCrop.set_img_info(
                        self.vdec_out_info.width,
                        self.vdec_out_info.height,
                        sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12,
                    )
                    ipeCrop.calc_param(
                        ipe_stream, cb_data.frame.data, pFaceCropIpeBufOut
                    )

                    # 特征模型预处理
                    transformMat = FeatureData.get_transform_mat(
                        bbox_instance.landmark, faceRect
                    )
                    ipeAffine = ipe_param_face_recongnition.IpeParamAffine(
                        model_width,
                        model_height,
                        transformMat,
                        0,
                        0,
                        0,
                    )
                    ipeAffine.set_img_info(
                        faceRect.w, faceRect.h, sdk.lyn_pixel_format_t.LYN_PIX_FMT_RGB24
                    )

                    ipeAffine.calc_param(ipe_stream, pFaceCropIpeBufOut, ipe_buffer)

                    sdk.lyn_record_event(ipe_stream, ipe_event_end)
                    sdk.lyn_stream_add_callback(
                        ipe_stream,
                        cal_event_cost,
                        [
                            ipe_event_begin,
                            ipe_event_end,
                            self.cost_info.get("face_feature_preprocess", []),
                        ],
                    )

                    pDevFeatureApuBuf_seek = sdk.lyn_addr_seek(
                        pDevFeatureApuBuf, apu_output_size * len(faceIndexVec)
                    )

                    sdk.lyn_record_event(ipe_stream, ipe_event)
                    sdk.lyn_stream_wait_event(apu_stream, ipe_event)

                    sdk.lyn_record_event(apu_stream, apu_event_begin)
                    common.error_check(
                        sdk.lyn_execute_model_async(
                            apu_stream,
                            face_recognize_model,
                            ipe_buffer,
                            pDevFeatureApuBuf_seek,
                            batch_size,
                        )
                    )
                    sdk.lyn_record_event(apu_stream, apu_event_end)
                    sdk.lyn_stream_add_callback(
                        apu_stream,
                        cal_event_cost,
                        [
                            apu_event_begin,
                            apu_event_end,
                            self.cost_info.get("face_feature_infer", []),
                        ],
                    )

                    common.error_check(
                        sdk.lyn_stream_add_callback(
                            apu_stream,
                            free_device_memory,
                            pFaceCropIpeBufOut,
                        )
                    )

                    common.error_check(
                        sdk.lyn_stream_add_callback(
                            apu_stream, return_buffers, [ipe_buffer_pool, [ipe_buffer]]
                        )
                    )

                    common.error_check(
                        sdk.lyn_stream_add_callback(
                            apu_stream, ipe_destory_callback, [ipeCrop, ipeAffine]
                        )
                    )

                    faceIndexVec.append(i)

            sdk.lyn_record_event(apu_stream, apu_event)
            sdk.lyn_stream_wait_event(osd_stream, apu_event)
            # 人脸比对
            sdk.lyn_record_event(osd_stream, post_event_begin)
            if len(faceIndexVec) > 0:
                pDevCosineBuf, ret = sdk.lyn_malloc(
                    len(faceIndexVec)
                    * (ctypes.sizeof(ctypes.c_int16) + ctypes.sizeof(ctypes.c_float))
                )
                common.error_check(ret)

                ret = sdk.lyn_execute_cosine_async(
                    osd_stream,
                    sdk.lyn_trans_type_t.LYN_TRANS,
                    len(faceIndexVec),
                    len(self.face_lib_labels),
                    1,
                    pDevFeatureApuBuf,
                    sdk.lyn_data_type_t.DT_FLOAT16,
                    face_lib_features,
                    sdk.lyn_data_type_t.DT_FLOAT16,
                    pDevCosineBuf,
                )
                common.error_check(ret)

                pHostCosineBuf_np = np.zeros(
                    len(faceIndexVec)
                    * (ctypes.sizeof(ctypes.c_int16) + ctypes.sizeof(ctypes.c_float))
                    , dtype=np.uint8)
                pHostCosineBuf = sdk.lyn_numpy_to_ptr(pHostCosineBuf_np)
                common.error_check(
                    sdk.lyn_memcpy_async(
                        osd_stream,
                        pHostCosineBuf,
                        pDevCosineBuf,
                        len(faceIndexVec)
                        * (ctypes.sizeof(ctypes.c_int16) + ctypes.sizeof(ctypes.c_float)),
                        sdk.lyn_memcpy_dir_t.ServerToClient,
                    )
                )

                pHostCosineBuf_c= pythonapi.PyCapsule_GetPointer(pHostCosineBuf , None)
                common.error_check(
                    sdk.lyn_stream_add_callback(
                        osd_stream,
                        face_recog_callback,
                        [pDevCosineBuf, pHostCosineBuf_c, faceIndexVec,
                        pDevFeatureApuBuf, pHostBoxesInfo, self.face_lib_labels],
                    )
                )

                sdk.lyn_synchronize_stream(osd_stream)
                # common.error_check(sdk.lyn_free(pDevFeatureApuBuf))
            else:
                if pDevFeatureApuBuf:
                    common.error_check(sdk.lyn_free(pDevFeatureApuBuf))

            sdk.lyn_record_event(osd_stream, post_event_end)
            sdk.lyn_stream_add_callback(
                osd_stream,
                cal_event_cost,
                [
                    post_event_begin,
                    post_event_end,
                    self.cost_info.get("face_feature_postprocess", []),
                ],
            )

            del cb_data.host_box_info
            cb_data.host_box_info = pHostBoxesInfo
            if pHostBoxesInfo.boxesnum > 0 and self.show_type < 2:
                pHostBoxesInfo_np = np.frombuffer(
                    pHostBoxesInfo,
                    dtype=np.uint8,
                    count=ctypes.sizeof(Box) // np.dtype(np.uint8).itemsize,
                )

                pHostBoxesInfo_ptr = sdk.lyn_numpy_to_ptr(pHostBoxesInfo_np)
                pDevBoxesInfo, ret = sdk.lyn_malloc(ctypes.sizeof(Box))
                common.error_check(
                    sdk.lyn_memcpy_async(
                        osd_stream,
                        pDevBoxesInfo,
                        pHostBoxesInfo_ptr,
                        ctypes.sizeof(Box),
                        sdk.lyn_memcpy_dir_t.ClientToServer,
                    )
                )

                sdk.lyn_synchronize_stream(osd_stream)

                pDevBoxesInfo_ptr = pythonapi.PyCapsule_GetPointer(pDevBoxesInfo, None)
                decodeImg_data_ptr = pythonapi.PyCapsule_GetPointer(
                    cb_data.frame.data, None
                )
                osd_para = struct.pack(
                    "1P3i1P4i",
                    pDevBoxesInfo_ptr,
                    self.vdec_out_info.width,
                    self.vdec_out_info.height,
                    sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12,
                    decodeImg_data_ptr,
                    24,
                    4,
                    2,
                    4,
                )
                common.error_check(
                    sdk.lyn_plugin_run_async(
                        osd_stream,
                        self.osd_plugin,
                        "lynDrawBoxAndText",
                        osd_para,
                        len(osd_para),
                    )
                )

                common.error_check(
                    sdk.lyn_stream_add_callback(
                        osd_stream,
                        free_device_memory,
                        pDevBoxesInfo,
                    )
                )

            sdk.lyn_stream_add_callback(
                osd_stream, put_to_queue, [self.__recognize_result_queue, cb_data]
            )
            if cancel_flag.value:
                break

        sdk.lyn_synchronize_stream(ipe_stream)
        sdk.lyn_synchronize_stream(apu_stream)
        sdk.lyn_synchronize_stream(osd_stream)
        sdk.lyn_free(face_lib_features)
        sdk.lyn_destroy_event(ipe_event)
        sdk.lyn_destroy_event(apu_event)
        sdk.lyn_destroy_stream(ipe_stream)
        sdk.lyn_destroy_stream(apu_stream)
        sdk.lyn_destroy_stream(osd_stream)
        sdk.lyn_unload_model(face_recognize_model)
        sdk.lyn_cosine_uninit()

        sdk.lyn_destroy_event(ipe_event_begin)
        sdk.lyn_destroy_event(ipe_event_end)
        sdk.lyn_destroy_event(apu_event_begin)
        sdk.lyn_destroy_event(apu_event_end)
        sdk.lyn_destroy_event(post_event_begin)
        sdk.lyn_destroy_event(post_event_end)

    def generate_result(self, cancel_flag):
        """存储结果或者用opencv显示"""
        sdk.lyn_set_current_context(self.__ctx)
        if self.show_type == 1:
            encoder = Encoder(
                self.__ctx, self.frame_pool, self.vdec_out_info, self.output_path
            )
        elif self.show_type == 0:
            opencv_window = OpencvWindow(
                self.frame_pool, self.video_frame, self.vdec_out_info
            )
        eos = False
        while not eos:
            cb_data = self.__recognize_result_queue.take()
            frame = cb_data.frame
            if cb_data.reconnect:
                self.__recognize_result_queue.clear()
                break
            eos = frame.eos
            if self.show_type == 0:
                opencv_window.show(frame)
            elif self.show_type == 1:
                encoder.encode(frame)
            else:
                # 保存json结果
                dump_box_json(self.output_path, cb_data.host_box_info)
                del cb_data.host_box_info
                self.frame_pool.push(frame.data)
            if cancel_flag.value:
                return
        if self.show_type == 1:
            del encoder

    def close(self):
        if self.__decode_send_thread.is_alive():
            self.__decode_send_thread.join()
        if self.__decode_recv_thread.is_alive():
            self.__decode_recv_thread.join()
        if self.__detect_thread.is_alive():
            self.__detect_thread.join()
        if self.__face_recognize_thread.is_alive():
            self.__face_recognize_thread.join()
        if self.__generate_result_thread.is_alive():
            self.__generate_result_thread.join()
        if self.__vdec_hdl != "":
            ret = sdk.lyn_vdec_close(self.__vdec_hdl)
        if self.__demux_hdl != "":
            sdk.lyn_demux_close(self.__demux_hdl)
        sdk.lyn_plugin_unregister(self.post_plugin)
        sdk.lyn_plugin_unregister(self.osd_plugin)
        sdk.lyn_destroy_context(self.__ctx)
